// jeString
// (c) jimon game studio

#ifndef JEH_JESTRING
#define JEH_JESTRING

#include "jeUnknown.h"
#include "jeTypes.h"
#include "jeDebug.h"
#include "jeMemory.h"
#include "jeMath.h"
#include "jeDynamicArray.h"
#include <stdio.h>

namespace je
{
	#define JE_STRING_ALLOCBLOCK_SIZE 32

	//! jeString
	template <typename T>
	class jeString:public je::jeUnknown
	{
	public:
		//! Chars Array Type
		typedef jeDynamicArray<T> CharsArrayType;
	protected:
		//! Chars Array
		CharsArrayType CharsArray;
	public:
		//! Constructor
		jeString()
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			CharsArray.InsertElement(0);
		}

		//! Constructor
		jeString(const jeString<T> & Other)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			(*this) = Other;
		}

		//! Constructor
		template <typename B>
		jeString(const B * Other)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			(*this) = Other;
		}

		//! Constructor
		template <typename B>
		jeString(const B * Other,u32 Length)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			for(u32 i=0;i<Length;i++)
				CharsArray.InsertElement((T)Other[i]);
			CharsArray.InsertElement(0);
		}

		//! Constructor
		jeString(f64 Other)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			c8 TempBuffer[512];

			#ifdef JE_MSVC
			_snprintf_s(TempBuffer,512,"%0.6f",Other);
			#else
			sprintf(TempBuffer,"%0.6f",Other);
			#endif

			(*this) = TempBuffer;
		}

		//! Constructor
		jeString(s32 Other)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			c8 TempBuffer[16];

			#ifdef JE_MSVC
			_snprintf_s(TempBuffer,16,"%i",Other);
			#else
			sprintf(TempBuffer,"%i",Other);
			#endif

			(*this) = TempBuffer;
		}

		//! Constructor
		jeString(u32 Other)
			:CharsArray(JE_STRING_ALLOCBLOCK_SIZE)
		{
			c8 TempBuffer[16];

			#ifdef JE_MSVC
			_snprintf_s(TempBuffer,16,"%u",Other);
			#else
			sprintf(TempBuffer,"%u",Other);
			#endif

			(*this) = TempBuffer;
		}

		//! Destructor
		~jeString()
		{
			CharsArray.Clear();
		}

		//! Get Chars Array
		inline const CharsArrayType & GetCharsArray() const
		{
			return CharsArray;
		}

		//! Get Chars Array
		inline CharsArrayType & GetCharsArray()
		{
			return CharsArray;
		}

		//! Get C String
		inline const T * c_str() const
		{
			return CharsArray.GetPtr();
		}

		//! Get C String
		inline T * c_str()
		{
			return CharsArray.GetPtr();
		}

		//! Get Size
		inline u32 GetSize() const
		{
			u32 Size = CharsArray.GetSize();
			if(Size > 0)
				return Size-1;
			else
				return 0;
		}

		//! Erase
		inline void Erase(u32 Index)
		{
			CharsArray.EraseElement(Index);
		}

		//! SubString
		inline jeString<T> SubString(u32 Begin,u32 Length) const
		{
			return jeString<T>(CharsArray.GetPtr()+Begin,Length);
		}

		//! Find
		inline u32 Find(const jeString<T> & This,u1 & Finded) const
		{
			for(u32 BeginPos=0;BeginPos<=(GetSize()-This.GetSize());BeginPos++)
			{
				u32 i=0;
				while((i<This.GetSize()) && (i<GetSize()) && (CharsArray[BeginPos+i] == This[i]))
					i++;

				if(i == This.GetSize())
				{
					Finded = true;
					return BeginPos;
				}
			}
			Finded = false;
			return 0;
		}

		//! Find
		inline u32 Find(T Char,u1 & Finded) const
		{
			for(u32 i=0;i<GetSize();i++)
			{
				if(CharsArray[i] == Char)
				{
					Finded = true;
					return i;
				}
			}
			Finded = false;
			return 0;
		}

		//! Replace First
		inline void ReplaceFirst(const jeString<T> & ReplaceThis,const jeString<T> & ReplaceWith)
		{
			u1 Finded = false;
			u32 BeginPos = Find(ReplaceThis,Finded);
			if(Finded)
			{
				jeString<T> Str1 = SubString(0,BeginPos);
				jeString<T> Str2 = ReplaceWith;
				jeString<T> Str3 = SubString(BeginPos+ReplaceThis.GetSize(),GetSize()-BeginPos-ReplaceThis.GetSize());
				(*this) = Str1+Str2+Str3;
			}
		}

		//! Append String
		inline void AppendString(const jeString<T> & Other)
		{
			CharsArray.EraseElement(GetSize());
			u32 i=0;
			while(Other[i])
				CharsArray.InsertElement(Other[i++]);
			CharsArray.InsertElement(0);
		}

		//! Append String
		template <typename B>
		inline void AppendString(const B * Other)
		{
			CharsArray.EraseElement(GetSize());
			u32 i=0;
			while(Other[i])
				CharsArray.InsertElement((T)Other[i++]);
			CharsArray.InsertElement(0);
		}

		//! Equal
		inline u1 Equal(const jeString<T> & Other) const
		{
			if(GetSize() != Other.GetSize())
				return false;
			return !memcmp(CharsArray.GetPtr(),Other.GetCharsArray().GetPtr(),GetSize()+1);
		}

		//! Equal
		template <typename B>
		inline u1 Equal(const B * Other) const
		{
			u32 i=0;
			while(Other[i])
			{
				if((i>=CharsArray.GetSize()) || (CharsArray[i] != (T)Other[i]))
					return false;
				i++;
			}
			if((CharsArray.GetSize()-1) == i)
				return true;
			else
				return false;
		}

		//! Operator =
		inline jeString<T> & operator = (const jeString<T> & Other)
		{
			if(this != &Other)
				CharsArray.CopyFrom(Other.GetCharsArray());
			return (*this);
		}

		//! Operator =
		template <typename B>
		inline jeString<T> & operator = (const B * Other)
		{
			CharsArray.Clear();
			u32 i=0;
			while(Other[i])
				CharsArray.InsertElement((T)Other[i++]);
			CharsArray.InsertElement(0);
			return (*this);
		}

		//! Operator +
		inline jeString<T> operator + (const jeString<T> & Other) const
		{
			jeString<T> This(*this);
			This.AppendString(Other);
			return This;
		}

		//! Operator +
		template <typename B>
		inline jeString<T> operator + (const B * Other) const
		{
			jeString<T> This(*this);
			This.AppendString(Other);
			return This;
		}

		//! Operator ==
		inline u1 operator == (const jeString<T> & Other) const
		{
			return Equal(Other);
		}

		//! Operator ==
		template <typename B>
		inline u1 operator == (const B * Other) const
		{
			return Equal(Other);
		}

		//! Operator !=
		inline u1 operator != (const jeString<T> & Other) const
		{
			return !Equal(Other);
		}

		//! Operator !=
		template <typename B>
		inline u1 operator != (const B * Other) const
		{
			return !Equal(Other);
		}

		//! Operator +=
		inline jeString<T> & operator += (const jeString<T> & Other)
		{
			AppendString(Other);
			return (*this);
		}

		//! Operator +=
		template <typename B>
		inline jeString<T> & operator += (const B * Other)
		{
			AppendString(Other);
			return (*this);
		}

		//! Operator +=
		inline jeString<T> & operator += (T Other)
		{
			CharsArray.InsertElement(Other,CharsArray.GetSize()-1);
			return (*this);
		}

		//! Operator +=
		inline jeString<T> & operator += (f32 Other)
		{
			AppendString(jeString<T>(Other));
			return (*this);
		}

		//! Operator +=
		inline jeString<T> & operator += (s32 Other)
		{
			AppendString(jeString<T>(Other));
			return (*this);
		}

		//! Operator +=
		inline jeString<T> & operator += (u32 Other)
		{
			AppendString(jeString<T>(Other));
			return (*this);
		}

		//! Operator []
		inline T operator [] (u32 Index)
		{
			return CharsArray[Index];
		}

		//! Operator []
		inline const T operator [] (u32 Index) const
		{
			return CharsArray[Index];
		}

		//! Operator <
		inline u1 operator < (const jeString<T> & Other) const
		{
			u32 Size = jeMin(GetSize(),Other.GetSize());
			for(u32 i=0;i<Size;i++)
			{
				s32 Diff = CharsArray[i] - Other.GetCharsArray()[i];
				if(Diff)
					return Diff < 0;
			}
			return GetSize()<Other.GetSize();
		}
	};

	//! Typedef for character jeStrings
	typedef jeString<c8> jeStringc;

	//! Typedef for wide character jeStrings
	typedef jeString<je_wchar_t> jeStringw;

	//! define default system string
	typedef jeString<jeschar> jeStringd;
};

#endif
